/*
 * zip_extract_SNMP.c
 *
 * Created: 01.07.2013 17:51:19
 *  Author: ILYA
 */ 
#include "zip_extract_SNMP.h"
#include <stdlib.h>
#include <string.h>

char convertTo_errorStatus(const char error_code)
{
	return 0x00;
}

char convertTo_errorIndex(const char error_code)
{
	return 0x00;
}

void zip_snmp_packet(const char *snmp_packet, char **snmp_zipped_packet)
{
	TSzippedsnmp *ts_zippedsnmp = (TSzippedsnmp *)malloc(sizeof(TSzippedsnmp));
	const char *iterator_snmp_packet = snmp_packet;
	iterator_snmp_packet += 4;//discarding from start of snmp: sequence and packet length -and- discarding version type and length
	ts_zippedsnmp->version = *iterator_snmp_packet;//getting version
	if (ts_zippedsnmp->version == SNMP_V1)
	{		
		int local_len;
		iterator_snmp_packet += 2; //discarding "community name" type
		local_len = *iterator_snmp_packet; //getting "community name" length
		iterator_snmp_packet += (local_len+1); //discarding "community name" and its length
		ts_zippedsnmp->command = *iterator_snmp_packet;//getting "command"
		iterator_snmp_packet += 3; //discarding command, "PDU" length and "request ID" type
		local_len = *iterator_snmp_packet;//getting "request ID" length
		iterator_snmp_packet += (local_len+1); //discarding "request ID" and its length
		iterator_snmp_packet += 5; //discarding "integer-length-error status, and integer-length"
		ts_zippedsnmp->err_code = *iterator_snmp_packet; //get only "error index"
		iterator_snmp_packet += 2; //discarding "error index" and "sequence" of bindedVariables
		int lengthOFbindedVariables = *iterator_snmp_packet; //get "length" of binded variables
		unsigned char* localBindedVariables = (unsigned char *)malloc(MAX_SIZE_OF_BINDED_VARIABLES*sizeof(unsigned char));
		unsigned char sizeof_localbindedVariables = 0;
		unsigned char* iterator_bindedVariables = localBindedVariables;
		int is0or1 = 0; //this for sure, that adding 1 on any pointer, the pointer will stay on range
		for (int i = 0; i < lengthOFbindedVariables;)
		{
			if (i == 0)
			{
				i = 2; //discarding "length" of bindedVariables and "sequence" of 1-st binded variable
				iterator_snmp_packet += 2; //discarding "length" of bindedVariables and "sequence" of 1-st binded variable
			}
			local_len = *iterator_snmp_packet; //get "length" of 1-st(second, ...) binded variable
			i += (local_len + 1); //goto the next byte successive to 1-st(second, ...) binded variable end: (this only for- for loop)
			if(i >= lengthOFbindedVariables)			
			{
				is0or1 = 0;
			}
			else
			{
				is0or1 = 1;
			}
			iterator_snmp_packet += 2; //discarding "length" of 1-st(second, ...) binded variable and OID type
			local_len = *iterator_snmp_packet; //get "length" of OID
			//start of get or make the OID: see SNMP rule for large numbers
			/*get or make the OID: (the first two bytes, from total 3 byte)*/
			int oid_offset = 0; unsigned short oid_unitID = 0;
			if ((iterator_snmp_packet[local_len-2] & 0x80) == 0x80)//the SNNMP rule for large numbers
				oid_offset = 1;			
			if((iterator_snmp_packet[local_len-3-oid_offset] & 0x80) == 0x80)
			{
				if((iterator_snmp_packet[local_len-4-oid_offset] & 0x80) == 0x80)
				{
					oid_unitID = (iterator_snmp_packet[local_len-4-oid_offset] & 0x7F/*0x7F==~0x80*/) * 128 * 128;
				}
				oid_unitID += (iterator_snmp_packet[local_len-3-oid_offset] & 0x7F/*0x7F==~0x80*/) * 128;
			}
			oid_unitID += iterator_snmp_packet[local_len-2-oid_offset];
			memcpy(iterator_bindedVariables, &oid_unitID, sizeof(oid_unitID));
			iterator_bindedVariables += 2;
			sizeof_localbindedVariables += 2;
			/*get or make the OID: (the last one byte, from total 3 byte)*/
			if ((iterator_snmp_packet[local_len-2] & 0x80) == 0x80)//the SNNMP rule for large numbers
			{
				*iterator_bindedVariables = (iterator_snmp_packet[local_len-2] & 0x7F/*0x7F==~0x80*/) * 128
											+ iterator_snmp_packet[local_len - 1];
			} 
			else
			{
				*iterator_bindedVariables = iterator_snmp_packet[local_len - 1];
			}
			if (is0or1 == 1)
			{
				++iterator_bindedVariables;
			}
			++sizeof_localbindedVariables;
			//end of get or make the OID: see SNMP rule for large numbers
			
			/*in this switch we have the following <<confusing>> parameters
				1. local_len, which is length of OID
				2. iterator_snmp_packet, which is point on to length of OID
			*/
			switch(ts_zippedsnmp->command)
			{
				case SNMP_ASN1_PDU_GET_REQ:
				{
					/*no need to add 1 for iterator_snmp_packet, because local_len has that 1 number.
						iterator_snmp_packet[local_len] == 0  intended to check: is last number of OID equal to 0 or no (is this a scalar variable)
					*/
					if (iterator_snmp_packet[local_len] == 0) //is scalar variable?
					{
						iterator_snmp_packet += (local_len + is0or1 + 2);//goto start of next binded variable
					} 
					else
					{
						//must be return error  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
					}
					break;
				}
				case SNMP_ASN1_PDU_GET_RESP:
				{
					if (iterator_snmp_packet[local_len] == 0) //is scalar variable?
					{
						/*discarding OID length, OID, type of binded variable and go on length of current binded variable*/
						iterator_snmp_packet += (local_len + 1 + 1);
						local_len = *iterator_snmp_packet; //get "length" of current binded variable
						*iterator_bindedVariables = local_len;//write into pointer bindedVariables, the binded variable length
						++iterator_bindedVariables;
						++sizeof_localbindedVariables;
						++iterator_snmp_packet;//point to current binded variable value
						memcpy(iterator_bindedVariables, iterator_snmp_packet, local_len);
						if (is0or1 == 1)
						{
							iterator_bindedVariables += local_len;
							iterator_snmp_packet += local_len;
						}
						sizeof_localbindedVariables += local_len;						
					} 
					else
					{
						//must be return error  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
					}					
					break;
				}
				default:
				{
					i = lengthOFbindedVariables; //break the for loop
					break;					
				}
			}				
		}
		
		ts_zippedsnmp->length_bindedVariables = sizeof_localbindedVariables;
		*snmp_zipped_packet = (char *)malloc((sizeof_localbindedVariables * sizeof(char)) + sizeof(TSzippedsnmp));
		//int snmp_pointerOffset = 0;
		//memcpy((*snmp_zipped_packet + snmp_pointerOffset), &ts_zippedsnmp->version, sizeof(ts_zippedsnmp->version));
		//snmp_pointerOffset += sizeof(ts_zippedsnmp->version);
		//memcpy((*snmp_zipped_packet + snmp_pointerOffset), &ts_zippedsnmp->command, sizeof(ts_zippedsnmp->command));
		//snmp_pointerOffset += sizeof(ts_zippedsnmp->command);
		//memcpy((*snmp_zipped_packet + snmp_pointerOffset), &ts_zippedsnmp->err_code, sizeof(ts_zippedsnmp->err_code));
		//snmp_pointerOffset += sizeof(ts_zippedsnmp->err_code);
		//memcpy((*snmp_zipped_packet + snmp_pointerOffset), &ts_zippedsnmp->length_bindedVariables, 
				//sizeof(ts_zippedsnmp->length_bindedVariables));
		//snmp_pointerOffset += sizeof(ts_zippedsnmp->length_bindedVariables);
		//memcpy((*snmp_zipped_packet + snmp_pointerOffset), localBindedVariables,
				//ts_zippedsnmp->length_bindedVariables);
		memcpy((*snmp_zipped_packet), ts_zippedsnmp, sizeof(TSzippedsnmp));
		memcpy((*snmp_zipped_packet + sizeof(TSzippedsnmp)), localBindedVariables, ts_zippedsnmp->length_bindedVariables);
		free(localBindedVariables);
	}		
	else
	{
		*snmp_zipped_packet = NULL;
	}
}
void extract_snmp_packet(const char *snmp_zipped_packet, char **snmp_packet)
{
	const TSzippedsnmp *ts_zippedsnmp = (const TSzippedsnmp *)snmp_zipped_packet;
	const char *bindedVariables = snmp_zipped_packet + sizeof(TSzippedsnmp);
	const char *iterator_bindedVariables = bindedVariables;
	int bindedVariables_count = 0;
	int bindedVariables_added_OIDs_sizes = 0;
	int total_local_leng = 0;
	for (int i = 0; i < ts_zippedsnmp->length_bindedVariables; ++i)
	{
		unsigned short unit_ID;
		unsigned char param_ID;
		memcpy(&unit_ID, iterator_bindedVariables, sizeof(unit_ID));
		iterator_bindedVariables += sizeof(unit_ID);//discarding the first two bytes of OID
		param_ID = *iterator_bindedVariables;
		++iterator_bindedVariables;//discarding the tail of OID
		/*bindedVariables_added_OIDs_sizes is the total size which will be added by large OIDs (see the rule SNMP for large numbers)*/
		if ((unit_ID / (128*128)) >= 1)
		{
			bindedVariables_added_OIDs_sizes += 2;
		}
		else if(unit_ID / 128 >= 1)
		{
			bindedVariables_added_OIDs_sizes += 1;			
		}
		if ((param_ID & 0x80) == 0x80)
		{
			bindedVariables_added_OIDs_sizes += 1;
		}
		
		int local_leng = *iterator_bindedVariables;//get binded variable length
		total_local_leng += 1 + local_leng + 1;
		if (total_local_leng < ts_zippedsnmp->length_bindedVariables)//for to be sure, that iterator_bindedVariables pointer in range
		{
			iterator_bindedVariables += (local_leng+1);//discarding binded variable length, binded variable, and point to next binded variable			
		}
		++bindedVariables_count;
	}
	iterator_bindedVariables = bindedVariables;
	/*the size of snmp_packet will be:
	sequence(0x30) + length(0x**) + integer(0x02) + length(0x**) + version(ts_zippedsnmp->version)
	+ string(0x04) + length(0x**) + sizeof(community name(variable)) + command(ts_zippedsnmp->command)
	+ length(0x**) + integer(0x02) + length(0x**) + sizeof(request id(variable))
	+ integer(0x02) + length(0x**) + error_status(0x**) + integer(0x02) + length(0x**) + error_index(ts_zippedsnmp->err_code)
	+ sequence(0x30) + length(0x**) + 
	//and this is binded variables //there is need a loop  bindedVariables_count times
	sequence(0x30) + length(0x**) + Object(0x06) + length(0x**) + sizeof(SNMP_OID_HEAD(variable)) + 3(snmp OID tail(the number 3 can be more))
	+ 1(snmp last bit, for scalars this bit is 0) +
	+ type + sizeof(the i-binded Variable(variable));
	//in our case we have the sum of all binded variables, and that case we can discard sizeof(the i-binded Variable(variable)) from loop, 
	//and only add the variablesSize
	*/
	int t_length = 2+1+2+strlen(COMMUNITY_NAME) + 1 + 3 + sizeof(REQUEST_ID)/sizeof(REQUEST_ID[0]) + 5 + 1 + 2
					+ bindedVariables_count*(4 + sizeof(SNMP_OID_HEAD)/sizeof(SNMP_OID_HEAD[0]) + 3 + 1 + 1)
					+ bindedVariables_added_OIDs_sizes
					+ ts_zippedsnmp->length_bindedVariables;
	*snmp_packet = (char *)malloc(2 + t_length);
	char *iterator_snmp_packet = *snmp_packet;
	if (ts_zippedsnmp->version == SNMP_V1)
	{
		*iterator_snmp_packet = SNMP_SEQUENCE; ++iterator_snmp_packet;
		int l_length = t_length;
		*iterator_snmp_packet = l_length; ++iterator_snmp_packet; l_length = 0;
		*iterator_snmp_packet = SNMP_ASN1_INTEG; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = 0x01; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = ts_zippedsnmp->version; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = SNMP_ASN1_OC_STR; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = strlen(COMMUNITY_NAME); ++iterator_snmp_packet; ++l_length;
		memcpy(iterator_snmp_packet, COMMUNITY_NAME, strlen(COMMUNITY_NAME)); iterator_snmp_packet += strlen(COMMUNITY_NAME); l_length += strlen(COMMUNITY_NAME);
		*iterator_snmp_packet = ts_zippedsnmp->command; ++iterator_snmp_packet; ++l_length;
		t_length -= (l_length+1);
		*iterator_snmp_packet = t_length; ++iterator_snmp_packet; l_length = 0;
		*iterator_snmp_packet = SNMP_ASN1_INTEG; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = sizeof(REQUEST_ID)/sizeof(REQUEST_ID[0]); ++iterator_snmp_packet; ++l_length;
		memcpy(iterator_snmp_packet, REQUEST_ID, sizeof(REQUEST_ID)/sizeof(REQUEST_ID[0]));
		       iterator_snmp_packet += sizeof(REQUEST_ID)/sizeof(REQUEST_ID[0]); l_length += sizeof(REQUEST_ID)/sizeof(REQUEST_ID[0]);
		*iterator_snmp_packet = SNMP_ASN1_INTEG; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = 0x01; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = convertTo_errorStatus(ts_zippedsnmp->err_code); ++iterator_snmp_packet; ++l_length;//error_status
		*iterator_snmp_packet = SNMP_ASN1_INTEG; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = 0x01; ++iterator_snmp_packet; ++l_length;
		*iterator_snmp_packet = convertTo_errorIndex(ts_zippedsnmp->err_code); ++iterator_snmp_packet; ++l_length;//error_index
		*iterator_snmp_packet = SNMP_SEQUENCE; ++iterator_snmp_packet; ++l_length;
		t_length -= (l_length+1);
		*iterator_snmp_packet = t_length; ++iterator_snmp_packet; l_length = 0;
		iterator_bindedVariables = bindedVariables;
		for (int i = 0; i < t_length; ++i)
		{
			*iterator_snmp_packet = SNMP_SEQUENCE; ++iterator_snmp_packet; ++i;
			int l_l = 0;
			if (ts_zippedsnmp->command == SNMP_ASN1_PDU_GET_REQ)
			{
				l_l = 0; //there is not any variable
			} 
			else if(ts_zippedsnmp->command == SNMP_ASN1_PDU_GET_RESP)
			{
				l_l = iterator_bindedVariables[1]; //get the length of variable.
			}
//#pragma region goto making OID
			int local_bindedVariables_added_OIDs_sizes = 0;			
			unsigned short unit_ID;
			unsigned char param_ID;
			memcpy(&unit_ID, iterator_bindedVariables, sizeof(unit_ID));
			iterator_bindedVariables += sizeof(unit_ID);//discarding the first two bytes of OID
			param_ID = *iterator_bindedVariables;
			++iterator_bindedVariables;//discarding the tail of OID //when the pointer is invalid, the next for cycle will not be continued 
			/*bindedVariables_added_OIDs_sizes is the total size which will be added by large OIDs (see the rule SNMP for large numbers)*/
			if ((unit_ID / (128*128)) >= 1)
			{
				local_bindedVariables_added_OIDs_sizes += 2;
			}
			else if(unit_ID / 128 >= 1)
			{
				local_bindedVariables_added_OIDs_sizes += 1;
			}
			if ((param_ID & 0x80) == 0x80)
			{
				local_bindedVariables_added_OIDs_sizes += 1;
			}
//#pragma endregion goto making OID
			
			*iterator_snmp_packet = 2 + sizeof(SNMP_OID_HEAD) / sizeof(SNMP_OID_HEAD[0]) + 3 + local_bindedVariables_added_OIDs_sizes
									+ 2 + l_l; ++iterator_snmp_packet; ++i;//Varbind length
			*iterator_snmp_packet = SNMP_ASN1_OBJ_ID; ++iterator_snmp_packet; ++i;
			*iterator_snmp_packet = sizeof(SNMP_OID_HEAD) / sizeof(SNMP_OID_HEAD[0]) + 3
									+ local_bindedVariables_added_OIDs_sizes; ++iterator_snmp_packet; ++i;//length of OID
		
//#pragma region make OID
			memcpy(iterator_snmp_packet, SNMP_OID_HEAD, sizeof(SNMP_OID_HEAD)/sizeof(SNMP_OID_HEAD[0]));
			      iterator_snmp_packet += sizeof(SNMP_OID_HEAD)/sizeof(SNMP_OID_HEAD[0]); 
				  i += sizeof(SNMP_OID_HEAD)/sizeof(SNMP_OID_HEAD[0]);
			if ((unit_ID / (128*128)) >= 1)
			{
				*iterator_snmp_packet = ((unit_ID / (128*128)) | 0x80); ++iterator_snmp_packet; ++i;
				unsigned short n_unit_ID = unit_ID - ((unit_ID / (128*128))*(128*128));
				if (n_unit_ID / 128 >= 1)
				{
					*iterator_snmp_packet = ((n_unit_ID / 128) | 0x80); ++iterator_snmp_packet; ++i;
				}
				else
				{
					*iterator_snmp_packet = 0x80; ++iterator_snmp_packet; ++i;
				}
				n_unit_ID -= (n_unit_ID / 128)*128;
				*iterator_snmp_packet = n_unit_ID; ++iterator_snmp_packet; ++i;
			}
			else if(unit_ID / 128 >= 1)
			{
				*iterator_snmp_packet = ((unit_ID / 128) | 0x80); ++iterator_snmp_packet; ++i;
				*iterator_snmp_packet = unit_ID - ((unit_ID / 128) * 128); ++iterator_snmp_packet; ++i;
			}
			else
			{							
				*iterator_snmp_packet = unit_ID; ++iterator_snmp_packet; ++i;
			}
			if ((param_ID & 0x80) == 0x80)
			{
				*iterator_snmp_packet = ((param_ID / 128) | 0x80); ++iterator_snmp_packet; ++i;
				*iterator_snmp_packet = param_ID - ((param_ID / 128) * 128); ++iterator_snmp_packet; ++i;
			}
			else
			{				
				*iterator_snmp_packet = param_ID; ++iterator_snmp_packet; ++i;
			}
			//*iterator_snmp_packet = if(scalar)0x00;else other; ++iterator_snmp_packet; ++i;
//#pragma endregion make OID

			switch(param_ID) //last byte of OID
			{
				case 1://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa scalar
					*iterator_snmp_packet = 0x00; ++iterator_snmp_packet; ++i;					
				break;
				case 2://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa table
					*iterator_snmp_packet = 0x01; ++iterator_snmp_packet; ++i;
				break;
			}

			switch(ts_zippedsnmp->command)
			{
				case SNMP_ASN1_PDU_GET_REQ:
				{
					*iterator_snmp_packet = SNMP_ASN1_NUL; ++iterator_snmp_packet; ++i;
					*iterator_snmp_packet = 0x00; ++i; if(i < t_length) ++iterator_snmp_packet;
					break;
				}
				case SNMP_ASN1_PDU_GET_RESP:
				{	
					switch(param_ID) //make types
					{
						case 1://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa for type int
						{
							//*iterator_snmp_packet = 0x02; ++iterator_snmp_packet; ++i;
							unsigned char data_length = *iterator_bindedVariables; ++iterator_bindedVariables;//data1_length(data2_length...)
							*iterator_snmp_packet = data_length; ++iterator_snmp_packet; ++i;//data1_length(data2_length...)
							memcpy(iterator_snmp_packet, iterator_bindedVariables, data_length);//get value (same as data1(data2...))
							iterator_snmp_packet += data_length; i += data_length; iterator_bindedVariables += data_length;
							break;
						}
					}			
					break;
				}
				default:
				{
					i = t_length; //break the for loop
					break;					
				}
			}	
		}
		
	}
	else
	{
		*snmp_packet = NULL;
	}
}